package com.smart.common.excle.utils;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.smart.common.annotation.ExcelField;
import com.smart.common.entities.core.BaseEnum;
import com.smart.common.entities.file.UploadBody;
import com.smart.common.entities.file.UploadResult;
import com.smart.common.entities.option.SelectOption;
import com.smart.common.exception.SmartException;
import com.smart.common.excle.domain.ExcleFiledEntity;
import com.smart.service.FileService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.EnumUtils;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.ss.util.CellRangeAddressList;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Excle字段读取工具
 *
 * @author Administrator
 * @date 2023/08/08
 */
@Log4j2
public class SmartExcleUtil {
    /**
     * 验证器
     */
    private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();

    /**
     * 验证Excle字段的主键
     *
     * @param obj obj
     * @return {@code String}
     */
    public static <T> String validateEntity(T obj) {
        StringBuilder result = new StringBuilder();
        Set<ConstraintViolation<T>> set = VALIDATOR.validate(obj, Default.class);
        if (set != null && !set.isEmpty()) {
            for (ConstraintViolation<T> cv : set) {
                result.append(cv.getMessage().concat(","));
            }
            result.deleteCharAt(result.length() - 1);
        }
        return result.toString();
    }

    /**
     * 获取写入数据时的字段映射 得到的数据结构为左边字段代码 右边字段名称
     *
     * @param tClass 解析实体类
     * @return {@link Map}<{@link String}, {@link String}>
     */
    public static List<ExcleFiledEntity> getFieldList(Class<?> tClass) {
        Field[] fields = tClass.getDeclaredFields();
        ArrayList<ExcleFiledEntity> excleFiledEntities = new ArrayList<>();
        // 父类字段处理
        Class<?> superclass = tClass.getSuperclass();
        if (ObjectUtil.isNotNull(superclass)) {
            Field[] superFields = superclass.getDeclaredFields();
            for (Field field : superFields) {
                field.setAccessible(true);
                ExcelField annotation = field.getAnnotation(ExcelField.class);
                if (ObjectUtil.isNotNull(annotation)) {
                    ExcleFiledEntity excleFiledEntity = new ExcleFiledEntity().setFieldCode(field.getName()).setFieldName(annotation.name()).setWidth(annotation.width()).setFieldSort(annotation.sort());
                    excleFiledEntities.add(excleFiledEntity);
                }
            }
        }


        for (Field declaredField : fields) {
            declaredField.setAccessible(true);
            ExcelField annotation = declaredField.getAnnotation(ExcelField.class);
            if (ObjectUtil.isNotNull(annotation)) {
                ExcleFiledEntity excleFiledEntity = new ExcleFiledEntity().setFieldCode(declaredField.getName()).setFieldName(annotation.name()).setWidth(annotation.width()).setFieldSort(annotation.sort());
                excleFiledEntities.add(excleFiledEntity);
            }
        }
        return excleFiledEntities.stream().sorted(Comparator.comparing(ExcleFiledEntity::getFieldSort)).collect(Collectors.toList());
    }

    /**
     * 得到enum提交验证
     *
     * @param rowIndex 行号
     * @param colIndex 列表
     * @param clazz    对应的枚举类型
     * @return {@link HSSFDataValidation}
     */
    public static HSSFDataValidation getEnumFiledValidation(int rowIndex, int colIndex, Class<?> clazz) {
        try {
            CellRangeAddressList regions = new CellRangeAddressList(rowIndex, rowIndex + 5000, colIndex, colIndex);
            BaseEnum[] enumConstants = (BaseEnum[]) clazz.getEnumConstants();
            String[] array = Arrays.stream(enumConstants).map(BaseEnum::getLabel).toArray(String[]::new);
            DVConstraint constraint = DVConstraint.createExplicitListConstraint(array);
            HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint);
            dataValidation.createPromptBox("温馨提示", "禁止手动填写, 请在下拉框中进行选择");
            return dataValidation;
        } catch (Exception e) {
            log.warn(e.getMessage());
            return null;
        }
    }


    /**
     * 获取数据字典下拉选项验证规则
     *
     * @param rowIndex 行索引
     * @param colIndex 坳指数
     * @param dataList 数据列表
     * @return {@link HSSFDataValidation}
     */
    public static HSSFDataValidation getDictFiledValidation(int rowIndex, int colIndex, List<SelectOption> dataList) {
        try {
            CellRangeAddressList regions = new CellRangeAddressList(rowIndex, rowIndex + 5000, colIndex, colIndex);
            String[] array = dataList.stream().map(SelectOption::getLabel).toArray(String[]::new);
            DVConstraint constraint = DVConstraint.createExplicitListConstraint(array);
            HSSFDataValidation dataValidation = new HSSFDataValidation(regions, constraint);
            dataValidation.createPromptBox("温馨提示", "禁止手动填写, 请在下拉框中进行选择");
            return dataValidation;
        } catch (Exception e) {
            log.warn(e.getMessage());
            return null;
        }
    }


    /**
     * 读取Excle表格内所属数据 并且判断数据表格是否为空或者是否超过5000条数据
     *
     * @param inputStream 输入流
     * @param sheetIndex  表索引
     * @param beanType    需要解析的表格随想
     * @return {@link List}<{@link T}>
     */
    public static <T> List<T> readAllList(InputStream inputStream, int sheetIndex, Class<T> beanType) {
        try {
            ExcelReader reader = ExcelUtil.getReader(inputStream, sheetIndex);
            List<ExcleFiledEntity> headerMap = getFieldList(beanType);
            headerMap.forEach(x -> reader.addHeaderAlias(x.getFieldName(), x.getFieldCode()));
            List<T> ts = reader.readAll(beanType);
            Assert.isTrue(ts.size() > 0, "导入的表格内没有任何数据");
            Assert.isTrue(ts.size() <= 5000, "单次导入的数据不能超过5000条");
            return ts;
        } catch (Exception e) {
            throw new SmartException("读取Excle 文件出错:" + e.getMessage());
        }
    }

    /**
     * 获取模板导出数据流
     *
     * @param beanType       bean类型
     * @param validationList 验证规则列表
     * @param mockData       模拟数据
     * @return {@link ByteArrayOutputStream}
     */
    public static ByteArrayOutputStream getTemplateStream(Class<?> beanType, List<HSSFDataValidation> validationList, Object mockData) {
        try {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            List<ExcleFiledEntity> headerMap = SmartExcleUtil.getFieldList(beanType);
            //创建一行模拟数据
            ExcelWriter writer = ExcelUtil.getWriter();
            headerMap.forEach(x -> writer.addHeaderAlias(x.getFieldCode(), x.getFieldName()));
            //注册约束列表
            Opt.ofEmptyAble(validationList).ifPresent(x -> x.forEach(writer::addValidationData));
            //写入模拟数据
            Opt.ofNullable(mockData).ifPresent(x -> writer.write(List.of(x)));
            //设置列宽
            for (int i = 0; i < headerMap.size(); i++) {
                writer.setColumnWidth(i, headerMap.get(i).getWidth());
            }
            //设置自动换行
            writer.getCellStyle().setWrapText(true);
            writer.setDefaultRowHeight(25);
            writer.flush(stream, true).close();
            return stream;
        } catch (Exception e) {
            throw new SmartException("生成EXCLE导入模板出错:" + e.getMessage());

        }
    }


    /**
     * 得到数据导出的流对象
     *
     * @param exportDataList 导出数据列表
     * @param validationList 验证列表 如果不进行下拉清单处理 那么开始进行请传入一个空对象
     * @return {@link ByteArrayOutputStream}
     */
    public static ByteArrayOutputStream getExportStream(List<?> exportDataList, List<HSSFDataValidation> validationList) {
        try {
            Assert.notEmpty(exportDataList, "没有可以导出的数据");
            Class<?> aClass = exportDataList.get(0).getClass();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            List<ExcleFiledEntity> headerMap = SmartExcleUtil.getFieldList(aClass);
            ExcelWriter writer = ExcelUtil.getWriter();
            //设置表头别名
            headerMap.forEach(x -> writer.addHeaderAlias(x.getFieldCode(), x.getFieldName()));
            Opt.ofEmptyAble(validationList).ifPresent(x -> x.forEach(writer::addValidationData));
            //写入数据
            writer.write(exportDataList, true);
            //设置列宽
            for (int i = 0; i < headerMap.size(); i++) {
                writer.setColumnWidth(i, headerMap.get(i).getWidth());
            }
            //设置行高
            for (int i = 0; i < exportDataList.size() + 10; i++) {
                writer.setRowHeight(i, 25);
            }
            //设置自动换行
            writer.getCellStyle().setWrapText(true);
            writer.flush(outputStream, true).close();
            return outputStream;
        } catch (Exception e) {
            throw new SmartException("Excle数据导出失败:" + e.getMessage());
        }
    }


    /**
     * 获取模板Excle文件下载地址
     *
     * @param fileService 文件服务
     * @param stream      流
     * @param fileName    文件名称
     * @return {@link String}
     */
    public static String getDownLoadUrl(FileService fileService, ByteArrayOutputStream stream, String fileName) {
        try {
            UploadBody.ByteFile byteFile = new UploadBody.ByteFile().setData(stream.toByteArray()).setSince("EXCLE").setContentType(ExcelUtil.XLSX_CONTENT_TYPE);
            UploadResult uploadResult = fileService.uploadFileByByte(byteFile, "EXCLE", fileName);
            return uploadResult.getUrl();
        } catch (Exception e) {
            throw new SmartException("上传EXCLE文件到OSS服务器出错:" + e.getMessage());
        }
    }


    public static <E extends Enum<E>> E getEnumByLabel(Class<E> enumClass, String label) {

        List<E> enumList = EnumUtils.getEnumList(enumClass);
        Optional<E> getLabel = enumList.stream().filter(x -> {
            try {
                return x.getClass().getMethod("getLabel").invoke(x).toString().equals(label);
            } catch (Exception e) {
                return false;
            }
        }).findFirst();

        return getLabel.orElse(null);

    }


    /**
     * 检查数据字典可选值是否正确 并且返回对应的代码值
     *
     * @param mapKey      在字典map 内的KEY对象
     * @param name        中文字段名称
     * @param sourceValue EXCLE表格内 源值
     * @param dictOptions 数据字典Map选项
     * @return {@link String}
     */
    public static String getDictOptions(String mapKey, String name, String sourceValue, Map<String, List<SelectOption>> dictOptions) {
        List<SelectOption> organOptions = dictOptions.get(mapKey);
        Assert.notEmpty(organOptions, "当前系统没有可用的" + name + "信息");
        Optional<SelectOption> first = organOptions.stream().filter(x -> x.getLabel().equals(sourceValue)).findFirst();
        if (first.isPresent()) {
            return first.get().getValue();
        } else {
            throw new RuntimeException(name + "数据匹配失败");
        }
    }

}
